#include "daliesque2.h"
#include "../metric/xyz.h"
#include <assert.h>
#include "aa_util.h"
#include "../metric/pairwise_distance_neighborhood.h"
#include "../pdb_index/aa_table.h"
#include "substitution_matrix.h"

void daliesque2_constructor(const char* pdbCode,
			   void * neighp,
			   struct store * atomStore,
			   off_t center,
			   off_t neighbors[],
			   short count) {
  struct daliesque2 * neigh = neighp;
  //void daliesque2_constructor(struct daliesque2 * d, struct xyz_int * center, unsigned char center_type_index, 
  //			   struct xyz_int neighbors[], unsigned char neighbor_type_indices[], size_t count) {
  

  struct xyz_int center_xyz; 
  struct xyz_int neighbor_xyz[DALI2_COUNT];

  struct atom * a;

  off_t start, stop;

  short i;

  short * sorted = malloc(sizeof(short) * count);

  assert(count == DALI2_COUNT);

  find_aa_range(atomStore, center, &start, &stop);
  
  calculate_aa_mass_center(atomStore, start, stop, &center_xyz);

  for(i = 0; i < count && neighbors[i] != -1; i++) {
    find_aa_range(atomStore, neighbors[i], &start, &stop);
    calculate_aa_mass_center(atomStore, start, stop, &(neighbor_xyz[i]));
  }

  a = store_read(atomStore, center);

  neigh->aa_residue_serial = a->residue;
  neigh->chain = a->chain;
  neigh->aa_indices[0] = aa_get_table_offset(a->residue_type);
  store_release(atomStore, center);
  strncpy(neigh->pdbStructure, pdbCode, 4);

  pairwise_distance_neighborhood_calculate( ((struct daliesque2*)neigh) ->paird, sorted, &center_xyz, (char*)neighbor_xyz, DALI2_COUNT, sizeof(struct xyz_int), (double(*)(const void*,const void*))xyz_int_distance);

  //  daliesque2_print_thorough(neigh, 0);

  for(i = 0; i < DALI2_COUNT; i++) {
    a = store_read(atomStore, neighbors[sorted[i]]);
    neigh->aa_indices[i+1] = aa_get_table_offset(a->residue_type);
    store_release (atomStore, neighbors[sorted[i]]);
  }
  
  free(sorted);



  /*  d->aa_indices[0] = center_type_index;
  for(i = 0; i < DALI2_COUNT; i++) {
    d->aa_indices[i+1] = neighbor_type_indices[i]; 
    }*/
}

double daliesque2_distance(struct daliesque2 * d1, struct daliesque2 * d2) {
  double ret = 0.0;
  double ret2 = 0.0;
  int i;

  ret = pairwise_distance_neighborhood_distances(d1->paird, d2->paird, pairwise_distance_neighborhood_get_array_size(DALI2_COUNT));


  for(i = 0 ; i < DALI2_COUNT + 1; i++) {
    ret2 = substitution_matrix_distance(d1->aa_indices[i], d2->aa_indices[i]);
  }

  
  //  printf("%f\t%f\n", ret, ret2);

  /*  for(i = 0; i < DALI2_COUNT+1; i++) {
      ret += aa_type_distances(d1->aa_indices[i], d2->aa_indices[i]);
      }*/
  return ret + (20.0 * ret2); // weight determined by 'eye-balling' the distribution of distances.
}


int daliesque2_can_calculate_neighborhood(struct store * s, off_t i) {
  off_t start, stop;

  find_aa_range(s,i,&start,&stop);

  if(find_in_aa_by_name(s, i, " CA ") != -1) {

    return can_calculate_aa_mass_center(s, start, stop);
  } else {

    return 0;
  }

}

const void * daliesque2_print(const void * dp, void * store) {
  const struct daliesque2 * d = dp;
  printf("%.4s\t%c\t%d", d->pdbStructure, d->chain, d->aa_residue_serial);

  return d;
}

void * daliesque2_print_thorough(struct daliesque2 * d, void * store) {
  int i;
  printf("%.4s\t%d [", d->pdbStructure, d->aa_residue_serial);

  for(i = 0; i < (DALI2_COUNT*(DALI2_COUNT+1))/2; i++) {
    printf("\t%d", d->paird[i]);
  }
  
  printf ("]\n");
  return d;
}
